// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.
// http://code.google.com/p/protobuf/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Author: Robert Blackwood (ported from Kenton's)
//  Based on original Protocol Buffers design by
//  Sanjay Ghemawat, Jeff Dean, and others.

#include <google/protobuf/compiler/as3/as3_service.h>
#include <google/protobuf/compiler/as3/as3_helpers.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/stubs/strutil.h>

namespace google {
namespace protobuf {
namespace compiler {
namespace as3 {

ServiceGenerator::ServiceGenerator(const ServiceDescriptor* descriptor)
  : descriptor_(descriptor) {}

ServiceGenerator::~ServiceGenerator() {}

void ServiceGenerator::Generate(io::Printer* printer) {

  printer->Print("// Generated by the protocol buffer compiler.  DO NOT EDIT!\n"
	  "\n");
  printer->Print("package $package$ {\n"
	  "\n",
	  "package", FileAs3Package(descriptor_->file()));
  printer->Indent();
  printer->Print("import com.google.protobuf.*;\n");
  printer->Print("import flash.utils.*;\n");
  printer->Print("import com.hurlant.math.BigInteger;\n");

  for (int i = 0; i < descriptor_->method_count(); i++) {
	  const MethodDescriptor* method = descriptor_->method(i);
	  printer->Print("import $class$;\n","class",ClassNameEx(descriptor_->method(i)->input_type()));
  }

  printer->Print(
    "public class $classname$_Service "
    "extends com.google.protobuf.Service {\n",
    "classname", descriptor_->name());
  printer->Indent();

  printer->Print("public function $classname$_Service(){\n",
				 "classname", descriptor_->name());
  printer->Indent();
  printer->Print("super(\"$classname$\" , $index$);\n", 
				 "classname",descriptor_->name(), 
				 "index", SimpleItoa(descriptor_->index()));
	
  for (int i = 0; i < descriptor_->method_count(); i++)
  {
	map<string, string> vars;
	vars["name"] = UnderscoresToCamelCase(descriptor_->method(i));
	vars["index"] =   SimpleItoa(descriptor_->method(i)->index());
	vars["input"] = descriptor_->method(i)->input_type()->name();
	vars["output"] =  descriptor_->method(i)->output_type()->name();
	printer->Print(vars,"_register_method(\"$name$\",$index$,\"$input$\",\"$output$\");\n") ;
	
  } 
  printer->Outdent();
  printer->Print("}\n");

  printer->Print("public function getRequestPrototype(method:MethodDescriptor):MessageLite {\n"
				 "  switch(method.index) {\n");
  	 
  for (int i = 0; i < descriptor_->method_count(); i++) {
	  const MethodDescriptor* method = descriptor_->method(i);
	  // Note:  down_cast does not work here because it only works on pointers,
	  //   not references.
	  printer->Print(
		  "    case $index$:\n"
		  "		return new $class$();\n",
		  "index",SimpleItoa(i),
		  "class",method->input_type()->name());	
  }
  printer->Print("    default:\n"
	  "      return null;\n"
	  "  }\n"
	  "}\n"
	  "\n");

  printer->Print("public function getResponsePrototype(method:MethodDescriptor):MessageLite {\n"
				 "  switch(method.index) {\n");

  for (int i = 0; i < descriptor_->method_count(); i++) {
	  const MethodDescriptor* method = descriptor_->method(i);
	  // Note:  down_cast does not work here because it only works on pointers,
	  //   not references.
	  printer->Print(
		  "    case $index$:\n"
		  "		return new $class$();\n",
		  "index",SimpleItoa(i),
		  "class",method->output_type()->name());	
  }
  printer->Print("    default:\n"
	  "      return null;\n"
	  "  }\n"
	  "}\n"
	  "\n");

  // Generate abstract method declarations.
  for (int i = 0; i < descriptor_->method_count(); i++) {
    const MethodDescriptor* method = descriptor_->method(i);
    map<string, string> vars;
    vars["name"] = UnderscoresToCamelCase(method);
	vars["input"] = descriptor_->method(i)->input_type()->name();
	vars["output"] =  descriptor_->method(i)->output_type()->name();
    printer->Print(vars,
      "public function $name$("
	  "controller:RpcController,"
	  "request:$input$,"
	  "response:$output$,"
	  "done:Closure) : void { throw new Error(\"this is base class cann`t use it\");};\n");
  }

  printer->Outdent();
  printer->Print("}\n\n");
	
  printer->Outdent();
  printer->Print("}\n\n");

  //GenerateStub(printer);
}

StubGenerator::StubGenerator(const ServiceDescriptor* descriptor)
: descriptor_(descriptor) {}

StubGenerator::~StubGenerator() {}

void 
StubGenerator::Generate(io::Printer* printer)
{
	printer->Print("// Generated by the protocol buffer compiler.  DO NOT EDIT!\n"
		"\n");
	printer->Print("package $package$ {\n"
		"\n",
		"package",  FileAs3Package(descriptor_->file()));
	printer->Indent();
	printer->Print("import com.google.protobuf.*;\n");
	printer->Print("import flash.utils.*;\n");
	printer->Print("import com.hurlant.math.BigInteger;\n");
	
	for (int i = 0; i < descriptor_->method_count(); i++) {
		const MethodDescriptor* method = descriptor_->method(i);
		printer->Print("import $class$;\n","class",ClassNameEx(descriptor_->method(i)->input_type()));
	}
	printer->Print("public class $classname$_Stub "
		"extends  $classname$_Service {\n",
		"classname", descriptor_->name());
	printer->Indent();
	printer->Print("private var _channel:RpcChannel = null;\n");
	printer->Print("public function $classname$_Stub( channel:RpcChannel){\n"
		"	this._channel = channel;\n"
		"}\n",
		"classname", descriptor_->name());
	
	printer->Print(
		"override public function callMethod(method:MethodDescriptor,"
		"controller:RpcController,"
		"request:MessageLite,"
		"response:MessageLite ,"
		"done:Closure ):void {\n"
		"  switch(method.index) {\n");
	printer->Indent();
	
	for (int i = 0; i < descriptor_->method_count(); i++) {
		const MethodDescriptor* method = descriptor_->method(i);
		map<string, string> sub_vars;
		sub_vars["name"] = method->name();
		sub_vars["index"] = SimpleItoa(i);
		sub_vars["input_type"] = method->input_type()->name();
		sub_vars["output_type"] = method->output_type()->name();

		// Note:  down_cast does not work here because it only works on pointers,
		//   not references.
		printer->Print(sub_vars,
			"    case $index$:\n"
			"		this.$name$(controller,request as $input_type$,response as $output_type$,done);\n"
			"      break;\n");
	}

	printer->Print("    default:\n"
		"      break;\n"
		"  }\n"
		"}\n"
		"\n");

	for (int i = 0; i < descriptor_->method_count(); i++) {
		const MethodDescriptor* method = descriptor_->method(i);
		map<string, string> vars;
		vars["index"] = SimpleItoa(i);
		vars["input"] = descriptor_->method(i)->input_type()->name();
		vars["output"] = descriptor_->method(i)->output_type()->name();
		vars["method"] = UnderscoresToCamelCase(method);
		printer->Print(vars,
			"\n"
			"override public function $method$("
			"controller:RpcController,"
			"request:$input$,"
			"response:$output$,"
			"done:Closure):void {\n"
			"  _channel.callMethod("
			"_methods[$index$],"
			"controller,"
			"request,"
			"response,"
			"done);\n"
			"}\n");
	}

	printer->Outdent();
	printer->Print("}\n\n");

	printer->Outdent();
	printer->Print("}\n\n");
}


}  // namespace as3
}  // namespace compiler
}  // namespace protobuf
}  // namespace google
